import random as rd
import pygame as pg

MAXNUM = 800  # 一次生成最大雨点数量
RAINNUM = MAXNUM  # 初始雨点数量最大
background = "back.png"  # 初始背景为夜晚、池塘；F10切换荷塘
back_music = "rain2.wav"  # 初始音效为池塘落雨，F10切换荷塘落雨
back_rainkind  = 0  # 初始雨滴为图形雨，F11切换代码雨
WIDTH, HEIGHT = 1300, 800  # 动画显示范围1300*800pdi


class RainDrop(object):  # 雨滴类
    def __init__(self, startX, startY, endY, rainColor, speed, size,
                 status, CircleR, CircleCurR, CircleSpeed):
        self.startX = startX  # 起始x，同时也是雨滴当前坐标x
        self.startY = startY  # 起始y，同时也是当前坐标y
        self.endY = endY  # 下落到水中坐标y
        self.rainColor = rainColor  # 颜色，RGB列表
        self.speed = speed  # 下落速度
        self.size = size  # 雨滴大小
        self.status = status  # 状态，0表示在空中，1表示落入水中
        self.CircleR = CircleR  # 水圈最大半径
        self.CircleCurR = CircleCurR  # 水圈当前半径
        self.CircleSpeed = CircleSpeed  # 水圈扩大速度


def CreatRain():  # 生成雨滴列表
    r = []
    for i in range(MAXNUM):  # 随机生成雨滴各项参数
        startX = rd.randint(-400, 1700)
        startY = rd.randint(0, 600)
        endY = rd.randint(0, 50) + 700
        rainColor = (rd.randint(0, 255), rd.randint(0, 255), rd.randint(0, 255))  # RGB，每个通道0~255
        speed = rd.randint(7, 15) / 10
        size = rd.randint(0, 3)
        status = 0
        CircleR = rd.randint(0, 30)
        CircleCurR = rd.randint(0, 2)
        CircleSpeed = rd.randint(0, 2) + 1
        drop = RainDrop(startX, startY, endY, rainColor, speed, size,
                 status, CircleR, CircleCurR, CircleSpeed)  # 创建对象
        r.append(drop)  # 加入到列表中
    return r


def refresh(p):  # 刷新雨滴
    p.startX = rd.randint(-400, 1700)
    p.startY = rd.randint(30, 200)
    p.endY = rd.randint(0, 50) + 700
    p.rainColor = (rd.randint(0, 255), rd.randint(0, 255), rd.randint(0, 255))
    p.speed = rd.randint(7, 15) / 10
    p.size = rd.randint(0, 3)
    p.status = 0
    p.CircleR = rd.randint(0, 30)
    p.CircleCurR = rd.randint(0, 2)
    p.CircleSpeed = rd.randint(0, 2) + 1


def updata_rain(p):  # 更新雨滴参数
    # 获取鼠标位置
    x, y = pg.mouse.get_pos()
    # 改变下雨速度
    p.startY += p.speed + y // 100
    p.speed = p.speed + 0.1
    # 改变雨滴斜率
    x = x - WIDTH // 2
    p.startX = p.startX + x // 100
    # 雨滴落入水中则更新状态
    if p.startY >= p.endY:
        p.status = 1


def draw_rain_trg(screen, p):  # 画三角形的雨滴
    # 更新雨滴参数
    updata_rain(p)
    # 获取鼠标位置
    x, y = pg.mouse.get_pos()
    x = x - WIDTH // 2
    x = x // 100
    y = y // 80
    # 三角形三个顶点的参数
    point = [(p.startX - 0.5 * p.size, p.startY),
             (p.startX + 0.5 * p.size, p.startY),
             (p.startX - x * (p.size + 1), p.startY - y * (p.size + 1) - 5)]
    # 将三角形画到screen上
    pg.draw.polygon(screen, p.rainColor, point, 0)


def draw_rain_bit(font, screen, p):  # 画比特的雨滴
    # 更新雨滴参数
    updata_rain(p)
    # 初始化0-1比特列表
    texts = [
        font.render('0', True, p.rainColor), font.render('1', True, p.rainColor)
    ]
    # 选取比特字符
    text = texts[p.endY % 2]
    # 将字符画到screen上
    screen.blit(text, (p.startX, p.startY))


def clear_circle(screen, p):  # 清除水圈
    # 画矩形内接椭圆需要的参数：第一个tuple包含矩形左上角坐标，第二个tuple包含矩形宽和高
    REC = ((p.startX - p.CircleCurR - 5, p.startY - p.CircleCurR + 5),
           (p.CircleCurR * 2 + 10, 2 * p.CircleCurR - 10))
    # 为了清除水圈，设置颜色为黑色
    color = (0, 0, 0)
    pg.draw.ellipse(screen, color, REC, 1)


def updata_circle(p):  # 更新水圈参数
    p.CircleCurR += p.CircleSpeed
    p.CircleSpeed += 0.1


def fall_water(screen, p):  # 落入水中，画水圈并更新雨滴参数
    # 如果水圈已达最大，则刷新雨滴参数
    if p.CircleCurR >= p.CircleR * (p.size + 1) // 4:
        refresh(p)
    else:
        # 否则清除上一个画过的水圈，并画出新的水圈
        clear_circle(screen, p)
        updata_circle(p)
        REC = ((p.startX - p.CircleCurR - 5, p.startY - p.CircleCurR + 5),
               (p.CircleCurR * 2 + 10, 2 * p.CircleCurR - 10))
        pg.draw.ellipse(screen, p.rainColor, REC, 1)


def fall_leaf_trg(screen, p):  # 落在荷叶上（三角形雨滴版）
    if p.CircleCurR >= p.CircleR:
        refresh(p)
    else:
        updata_circle(p)
        # 获取鼠标位置
        x, y = pg.mouse.get_pos()
        x = (x - WIDTH // 2) / 50
        y = y / 50
        size = p.size + 2
        # 水滴四溅的效果由四个位置变化的圆圈得到
        REC = ((p.startX + p.CircleCurR + x + rd.randint(10, 20),
                p.startY + p.CircleCurR - y - rd.randint(10, 20)), (size, size))
        pg.draw.ellipse(screen, p.rainColor, REC, 0)
        REC = ((p.startX + p.CircleCurR + x + rd.randint(10, 20),
                p.startY - p.CircleCurR - y - rd.randint(10, 20)), (size, size))
        pg.draw.ellipse(screen, p.rainColor, REC, 0)
        REC = ((p.startX - p.CircleCurR - x + rd.randint(10, 20),
                p.startY + p.CircleCurR - y - rd.randint(10, 20)), (size, size))
        pg.draw.ellipse(screen, p.rainColor, REC, 0)
        REC = ((p.startX - p.CircleCurR - x + rd.randint(10, 20),
                p.startY + p.CircleCurR - y - rd.randint(10, 20)), (size, size))
        pg.draw.ellipse(screen, p.rainColor, REC, 0)


def fall_leaf_bit(screen, p, font):  # 落在荷叶上（比特雨版）
    if p.CircleCurR >= p.CircleR:
        refresh(p)
    else:
        x, y = pg.mouse.get_pos()
        updata_circle(p)
        texts = [
            font.render('0', True, p.rainColor), font.render('1', True, p.rainColor)
        ]
        text = texts[p.endY % 2]
        x = (x - WIDTH // 2) / 50
        y = y / 50
        # 水珠四溅的效果由四个位置不同的比特字符得到
        screen.blit(text, (p.startX - p.CircleCurR - x + rd.randint(10, 20),
                           p.startY - p.CircleCurR - y - rd.randint(10, 20)))
        screen.blit(text, (p.startX - p.CircleCurR - x + rd.randint(10, 20),
                           p.startY + p.CircleCurR - y - rd.randint(10, 20)))
        screen.blit(text, (p.startX + p.CircleCurR + x - rd.randint(10, 20),
                           p.startY - p.CircleCurR - y - rd.randint(10, 20)))
        screen.blit(text, (p.startX + p.CircleCurR + x - rd.randint(10, 20),
                           p.startY + p.CircleCurR - y - rd.randint(10, 20)))


def thunder(screen):  # 打雷闪电函数
    # 一共两种闪电图案和打雷音效
    index = rd.choice([1, 2])
    # 播放音效
    thunder_wav = pg.mixer.Sound("thunder0" + str(index) + ".wav")
    thunder_wav.play()
    # 更换图片为白色图片，表示闪电
    back2 = pg.image.load("back2.png")
    screen.blit(back2, (0, 0))
    # 将图片更新到屏幕上
    pg.display.update()
    # 停留一会
    pg.time.delay(40)
    # 换回背景图
    back = pg.image.load(background)
    screen.blit(back, (0, 0))
    pg.display.update()
    # 将闪电图案更新到屏幕上
    thund = pg.image.load("thunder" + str(index) + ".png")
    pos = rd.randint(0, 800), rd.randint(0, 50)
    screen.blit(thund, pos)
    pg.display.update()
    pg.time.delay(20)


def rain():  # 控制下雨
    # 全局变量声明
    global r0
    global RAINNUM
    global background
    global back_music
    global back_rainkind
    # 获取需要处理的雨滴序列
    r = r0[0: RAINNUM]
    # 初始化pygame
    pg.init()
    pg.mixer.init()
    # 设置比特雨的字符格式（此处默认系统格式）
    font = pg.font.Font(None, 20)
    # 播放背景音乐，初始池塘音效
    pg.mixer.music.load(back_music)
    pg.mixer.music.play(-1)
    while True:
        screen = pg.display.set_mode((WIDTH, HEIGHT))
        for event in pg.event.get():
            x, y = pg.mouse.get_pos()
            # 根据鼠标位置设置音量大小，表示雨的强弱
            pg.mixer.music.set_volume(RAINNUM / 1000 + y / 2000)
            # 点击退出键退出
            if event.type == pg.QUIT:
                pg.quit()
                exit()
            elif event.type == pg.MOUSEBUTTONDOWN:
                # 鼠标左键打雷，并切换雨的数量
                if event.button == 1:
                    thunder(screen)
                    # 每次增加200，直到0重复循环
                    RAINNUM = (RAINNUM + 200) % 1000
                    # 0即不下雨，不下雨暂停播放声音
                    if RAINNUM == 0:
                        pg.mixer.music.pause()
                    else:
                        pg.mixer.music.unpause()
                    # 重新获取雨滴序列
                    r = r0[0: RAINNUM+1]
                else:
                    # 鼠标右键只打雷
                    thunder(screen)
            elif event.type == pg.KEYDOWN:
                # F11切换池塘与荷塘
                if event.key == pg.K_F11:
                    if background == "back.png":
                        background = "back3.png"
                    else:
                        background = "back.png"
                    pg.mixer.music.stop()
                    # 同步切换背景音乐
                    if back_music == "rain1.wav":
                        back_music = "rain2.wav"
                    else:
                        back_music = "rain1.wav"
                    pg.mixer.music.load(back_music)
                    pg.mixer.music.play(-1)
                    thunder(screen)
                # F10切换雨滴种类
                elif event.key == pg.K_F10:
                    if back_rainkind == 0:
                        back_rainkind = 1
                    else:
                        back_rainkind = 0
                    thunder(screen)
            # 画背景
            backg = pg.image.load(background).convert()
            screen.blit(backg, (0, 0))
            # 随机打雷
            if rd.randint(0, 10000) % 5000 == 1:
                thunder(screen)
            # 从雨滴序列中随机选取一定数量的雨滴画在屏幕上，循环进行一次结束后统一更新屏幕
            # 每次循环相当于画出一次关键帧，反复此过程实现关键帧动画
            for i in range(RAINNUM):
                # 随机选取雨滴对象
                p = rd.choice(r)
                if p.status == 0:
                    if back_rainkind == 0:
                        draw_rain_trg(screen, p)  # 画出三角形雨
                    else:
                        draw_rain_bit(font, screen, p)  # 画出比特雨
                else:
                    if background == "back.png":
                        fall_water(screen, p)
                    elif background == "back3.png":
                        if back_rainkind == 0:
                            fall_leaf_trg(screen, p)
                        else:
                            fall_leaf_bit(screen, p, font)
            # 将绘画统一更新到屏幕上显示，完成一次关键帧的绘制
            pg.display.flip()


# main 程序入口
# 生成雨滴序列
r0 = CreatRain()
# 下雨总函数
rain()
